package com.example.system.service.impl;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.alibaba.excel.EasyExcel;
import com.auth0.jwt.JWT;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.common.enums.CodeEnum;
import com.example.common.exception.ServiceException;
import com.example.common.utils.*;
import com.example.system.dto.UserDTO;
import com.example.system.entity.Menu;
import com.example.system.entity.Patient;
import com.example.system.entity.User;
import com.example.system.entity.UserDetail;
import com.example.system.listener.UserExcelReadListener;
import com.example.system.mapper.PatientMapper;
import com.example.system.mapper.RoleMapper;
import com.example.system.mapper.RoleMenuMapper;
import com.example.system.mapper.UserMapper;
import com.example.system.service.IMenuService;
import com.example.system.service.IUserService;
import com.example.system.service.UserService;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import org.apache.commons.codec.binary.Base64;
import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 王鹤蒲
 * @since 2023-07-31
 */
@Service
//@CacheConfig(cacheNames ={"userCache"} )
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService, UserService {

    private final static Logger log = LoggerFactory.getLogger(UserServiceImpl.class);

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private PatientMapper patientMapper;

    @Autowired
    private RoleMapper roleMapper;

    @Autowired
    private RoleMenuMapper roleMenuMapper;

    @Autowired
    private IMenuService iMenuService;

    @Autowired
    private DefaultKaptcha defaultKaptcha;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private UserExcelReadListener userExcelReadListener;

    @Autowired
    private SnowFlake snowFlake;

    @Autowired
    private JavaMailSender javaMailSender;

    @Value("${spring.mail.username}")
    private String from;

    private UserDetails userDetails;

    @Resource
    private Scheduler scheduler;


    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    //    @Cacheable(value="userCache1",key="#user+'_'+#currentPage+'_'+#pageSize")
    public Page<User> selectAll(User user, int currentPage, int pageSize) {
        Page<User> page = new Page<User>(currentPage, pageSize);
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<User>();
        lambdaQueryWrapper.eq(User::getDelFlag, '0');
        lambdaQueryWrapper.like(user.getUserId() != null, User::getUserId, user.getUserId());
        lambdaQueryWrapper.like(user.getUserName() != null, User::getUserName, user.getUserName());
        lambdaQueryWrapper.like(user.getPhonenumber() != null, User::getPhonenumber, user.getPhonenumber());
        lambdaQueryWrapper.like(user.getEmail() != null, User::getEmail, user.getEmail());
        lambdaQueryWrapper.like(user.getAddress() != null, User::getAddress, user.getAddress());
        userMapper.selectPage(page, lambdaQueryWrapper);
        return page;
    }

    //    @CacheEvict(value="userCache1",allEntries = true)
    @Transactional
    @Override
    public boolean deleteById(Integer id) {
        LambdaUpdateWrapper<User> lambdaUpdateWrapper = new LambdaUpdateWrapper<User>();
        lambdaUpdateWrapper.eq(User::getUserId, id);
        User user = new User();
        user.setDelFlag("1");
        user.setStatus(0);
        user.setUpdateBy(getCurrentLoginUser().getUserName());
        LocalDateTime updateTime = LocalDateTime.now();
        user.setUpdateTime(updateTime);
        return userMapper.update(user, lambdaUpdateWrapper) == 1 ? true : false;
    }

    @Override
    @Transactional
//    @CacheEvict(value="userCache1",allEntries = true)
    public boolean deleteByIds(Integer[] ids) {

        if (ids != null && ids.length > 0) {
            System.out.println(ids);
            LambdaUpdateWrapper<User> lambdaUpdateWrapper = new LambdaUpdateWrapper<User>();
            User user = new User();
            user.setDelFlag("1");
            user.setStatus(0);
            user.setUpdateBy(getCurrentLoginUser().getUserName());
            LocalDateTime updateTime = LocalDateTime.now();
            user.setUpdateTime(updateTime);
            for (Integer id : ids) {
                lambdaUpdateWrapper.eq(User::getUserId, id);
                userMapper.update(user, lambdaUpdateWrapper);
                lambdaUpdateWrapper.clear();
            }
            return true;
        } else {
            return false;
        }
    }

    @Override
    public boolean updateUser(UserDTO userDTO) {
        LambdaUpdateWrapper<User> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        lambdaUpdateWrapper.eq(User::getUserName, userDTO.getUserName());
        lambdaUpdateWrapper.set(StrUtil.isNotBlank(userDTO.getNickName()), User::getNickName, userDTO.getNickName());
        lambdaUpdateWrapper.set(StrUtil.isNotBlank(userDTO.getPhonenumber()), User::getPhonenumber, userDTO.getPhonenumber());
        lambdaUpdateWrapper.set(StrUtil.isNotBlank(userDTO.getEmail()), User::getEmail, userDTO.getEmail());
        lambdaUpdateWrapper.set(StrUtil.isNotBlank(userDTO.getAddress()), User::getAddress, userDTO.getAddress());
        lambdaUpdateWrapper.set(StrUtil.isNotBlank(userDTO.getSex()), User::getSex, userDTO.getSex());
        lambdaUpdateWrapper.set(StrUtil.isNotBlank(userDTO.getAvatar()), User::getAvatar, userDTO.getAvatar());
        lambdaUpdateWrapper.set(User::getUpdateBy, getCurrentLoginUser().getUserName());
        LocalDateTime updateTime = LocalDateTime.now();
        lambdaUpdateWrapper.set(User::getUpdateTime, updateTime);
        if(ObjectUtil.isNotEmpty(userDTO.getPatientId())){
            Patient patient= patientMapper.selectById( userDTO.getPatientId());
            if(ObjectUtil.isEmpty(patient.getIdNumber())){
             LambdaUpdateWrapper<Patient> patientLambdaUpdateWrapper=new LambdaUpdateWrapper<>();
             patientLambdaUpdateWrapper.eq(Patient::getId,patient.getId());
             patientLambdaUpdateWrapper.set(Patient::getIdNumber,userDTO.getIdNumber());
             DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
             LocalDate birthDate = LocalDate.parse(userDTO.getIdNumber().substring(6, 10) + "-" + userDTO.getIdNumber().substring(10, 12) + "-" + userDTO.getIdNumber().substring(12, 14), formatter);
             patientLambdaUpdateWrapper.set(Patient::getBirthDate,birthDate);
             patientLambdaUpdateWrapper.set(Patient::getUpdateTime,updateTime);
             patientMapper.update(new Patient(),patientLambdaUpdateWrapper);
            }
        }
        if (userMapper.update(new User(), lambdaUpdateWrapper) == 1) {
            return true;
        } else {
            return false;
        }
    }

    @Override
    public void export1(HttpServletResponse response) {
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<User>();
        lambdaQueryWrapper.eq(User::getDelFlag, '0');
        List<User> userList = userMapper.selectList(lambdaQueryWrapper);
        String fileName = "用户数据表" + String.valueOf(System.currentTimeMillis());
        String sheetName = "用户数据表";
        ExcelUtils.export(response, userList, fileName, User.class, sheetName);
    }

    @Override
    public R importExcel(MultipartFile file) {
        try {
            EasyExcel.read(file.getInputStream(), User.class, userExcelReadListener).sheet(0).headRowNumber(2).doRead();
        } catch (IOException e) {
            throw new ServiceException(CodeEnum.File_IMPORT_ERROR.getCode(), CodeEnum.File_IMPORT_ERROR.getMsg());
        }
        return R.success();
    }


    @Override
    public void export(User user, HttpServletResponse response) {

        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<User>();
        System.out.println(user);
        System.out.println(user.getUserName());
        lambdaQueryWrapper.eq(User::getDelFlag, '0');
        lambdaQueryWrapper.like(user.getUserId() != null, User::getUserId, user.getUserId());
        lambdaQueryWrapper.like(user.getUserName() != null, User::getUserName, user.getUserName());
        lambdaQueryWrapper.like(user.getPhonenumber() != null, User::getPhonenumber, user.getPhonenumber());
        lambdaQueryWrapper.like(user.getEmail() != null, User::getEmail, user.getEmail());
        lambdaQueryWrapper.like(user.getAddress() != null, User::getAddress, user.getAddress());
        List<User> userList = new ArrayList<User>();
        System.out.println(userList);
        userList = userMapper.selectList(lambdaQueryWrapper);
        ExcelWriter excelWriter = ExcelUtil.getWriter(true);
        excelWriter.setOnlyAlias(true);
        excelWriter.addHeaderAlias("userName", "用户名称");
        excelWriter.addHeaderAlias("nickName", "用户昵称");
        excelWriter.addHeaderAlias("password", "密码");
        excelWriter.addHeaderAlias("sex", "性别（0男 1女 2未知）");
        excelWriter.addHeaderAlias("userType", "用户类型（00系统用户)");
        excelWriter.addHeaderAlias("email", "用户邮箱");
        excelWriter.addHeaderAlias("phonenumber", "手机号码");
        excelWriter.addHeaderAlias("address", "联系地址");
        excelWriter.addHeaderAlias("avatar", "头像地址");
        excelWriter.addHeaderAlias("status", "帐号状态（0正常 1停用）");
        excelWriter.addHeaderAlias("loginIp", "最后登录IP");
        excelWriter.addHeaderAlias("loginDate", "最后登录时间");
        excelWriter.addHeaderAlias("createBy", "创建者");
        excelWriter.addHeaderAlias("createTime", "创建时间");
        excelWriter.addHeaderAlias("updateBy", "更新者");
        excelWriter.addHeaderAlias("updateTime", "更新时间");
        excelWriter.addHeaderAlias("remark", "备注");
        try {
            excelWriter.write(userList);
            String fileName = URLEncoder.encode("用户信息", "utf-8");
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml,sheet;charset=utf-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");
            response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
            ServletOutputStream outputStream = response.getOutputStream();
            excelWriter.flush(outputStream, true);
            excelWriter.close();
            outputStream.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public User getById(Integer userId) {
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<User>();
        lambdaQueryWrapper.eq(User::getDelFlag, '0');
        lambdaQueryWrapper.eq(User::getUserId, userId);
        return userMapper.selectOne(lambdaQueryWrapper);
    }


    @Override
    public User getUserInfo(UserDTO userDTO) {
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(User::getUserName, userDTO.getUserName());
        lambdaQueryWrapper.eq(User::getDelFlag, '0');
        return userMapper.selectOne(lambdaQueryWrapper);
    }

    public UserDTO getUserByUserName(String userName) {
        LambdaQueryWrapper<User> userLambdaQueryWrapper = new LambdaQueryWrapper<>();
        userLambdaQueryWrapper.eq(User::getUserName, userName);
        userLambdaQueryWrapper.eq(User::getDelFlag, '0');
        UserDTO userDTO = new UserDTO();
        User user=userMapper.selectOne(userLambdaQueryWrapper);
        BeanUtil.copyProperties(user, userDTO, true);
        if("ROLE_PATIENT".equals(user.getRole())){
        LambdaQueryWrapper<Patient> patientLambdaQueryWrapper=new LambdaQueryWrapper<>();
        patientLambdaQueryWrapper.eq(Patient::getUserId,user.getUserId());
            Patient patient=patientMapper.selectOne(patientLambdaQueryWrapper);
            Double account=patient.getAccount();
            Integer patientId=patient.getId();
            String idNumber=patient.getIdNumber();
            LocalDate birthDate=patient.getBirthDate();
            userDTO.setPatientId(patientId);
            userDTO.setIdNumber(idNumber);
            userDTO.setBirthDate(birthDate);
            userDTO.setAccount(account);
        }
        return userDTO;
    }


    @Override
    public UserDTO login(UserDTO userDTO) throws ClassNotFoundException {
        if (RedisUtil.get(userDTO.getUuid()) == null) {
            throw new ServiceException(CodeEnum.VERIFICATION_CODE_ERROR.getCode(), "验证码不存在，请刷新重新输入");
        }
        if (!RedisUtil.get(userDTO.getUuid()).equals(userDTO.getCode())) {
            throw new ServiceException(CodeEnum.VERIFICATION_CODE_ERROR.getCode(), "验证码错误，请刷新重新输入");
        }
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDTO.getUserName(), userDTO.getPassword());
        try {
           authenticationManager.authenticate(authenticationToken);
           userDetails=loadUserByUsername(userDTO.getUserName());
        } catch (AuthenticationException e) {
            log.error(e.getMessage());
            if(Objects.isNull(userDetails)||!passwordEncoder.matches(userDTO.getPassword(),userDetails.getPassword())){
                 updateFailedAttempts(userDTO.getUserName());
                 if(isAccountLocked(userDTO.getUserName())){
                     throw new ServiceException(CodeEnum.NO_USER_EXIST.getCode(),"该账号密码连续输错五次，已被暂时锁定10分钟，请稍后重试");
                 }
            }
            throw new ServiceException(CodeEnum.NO_USER_EXIST.getCode(), CodeEnum.NO_USER_EXIST.getMsg());
        }
        if(redisTemplate.hasKey("failedAttempts:"+userDTO.getUserName())){
             if(isAccountLocked(userDTO.getUserName())){
                 throw new ServiceException(CodeEnum.NO_USER_EXIST.getCode(),"该账号密码连续输错五次，已被暂时锁定10分钟，请稍后重试");
             }else{
                 clearFailedAttempts(userDTO.getUserName());
             }
        }
        User user = getUserInfo(userDTO);
        UserDTO userDTO1 = new UserDTO();
        BeanUtil.copyProperties(user, userDTO1, true);
        if("ROLE_PATIENT".equals(user.getRole())){
            LambdaQueryWrapper<Patient> patientLambdaQueryWrapper=new LambdaQueryWrapper<>();
            patientLambdaQueryWrapper.eq(Patient::getUserId,user.getUserId());
            Patient patient=patientMapper.selectOne(patientLambdaQueryWrapper);
            Double account=patient.getAccount();
            Integer patientId=patient.getId();
            String idNumber=patient.getIdNumber();
            userDTO1.setPatientId(patientId);
            userDTO1.setIdNumber(idNumber);
            userDTO1.setAccount(account);
        }
        String token = JwtUtils.genToken(String.valueOf(user.getUserId()), user.getPassword());
        RedisUtil.set(String.valueOf(user.getUserId()), token);
        RedisUtil.expire(String.valueOf(user.getUserId()), 60 * 60 * 2);
        userDTO1.setToken(token);
        String role = user.getRole();
        List<Menu> roleMenuList=getRoleMenuList(role);
        userDTO1.setMenuList(roleMenuList);
        LocalDateTime loginTime = LocalDateTime.now();
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
        User user1 = new User();
        user1.setUserId(user.getUserId());
        user1.setLoginIp(ServletUtil.getClientIP(request));
        user1.setLoginDate(loginTime);
        userMapper.updateById(user1);
        return userDTO1;

    }

    @Override
    public R sendEmailCode(String email) {
        SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
        simpleMailMessage.setFrom(from);
        simpleMailMessage.setTo(email);
        simpleMailMessage.setSubject("系统验证码");
        String dynamicCode = RandomUtil.randomNumbers(6);
        RedisUtil.set(email, dynamicCode);
        RedisUtil.expire(email, 300);
        simpleMailMessage.setText("动态码：" + dynamicCode + "。此账号只用于账号校验，5分钟内有效，如果非本人操作请忽略。");
        simpleMailMessage.setSentDate(new Date());
        javaMailSender.send(simpleMailMessage);
        return R.success();
    }

    @Override
    public R verifyEmailCode(String email, String dynamicCode) {
        if (dynamicCode.equals(RedisUtil.get(email))) {
            return R.success();
        } else {
            return R.error(CodeEnum.VERIFICATION_CODE_ERROR.getCode(), CodeEnum.VERIFICATION_CODE_ERROR.getMsg());
        }

    }

    @Override
    public List<Menu> getRoleMenuList(String role) {
        Integer roleId = roleMapper.selectByFlag(role);
        List<Integer> menuIdList = roleMenuMapper.selectMenuIdsByRoleId(roleId);
        List<Menu> menus = iMenuService.findAll(" ");
        List<Menu> menuList = new ArrayList<Menu>();
        for (Menu menu : menus) {
            if (menuIdList.contains(menu.getMenuId())) {
                menuList.add(menu);
            }
            List<Menu> submenuList = menu.getSubmenuList();
            submenuList.removeIf(submenu -> !menuIdList.contains(submenu.getMenuId()));
        }
        return menuList;
    }


    @Override
    public R save(UserDTO userDTO) {
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper();
        lambdaQueryWrapper.eq(User::getUserName, userDTO.getUserName());
        if (!userMapper.selectList(lambdaQueryWrapper).isEmpty()) {
            return R.error(CodeEnum.USERNAME_EXIST.getCode(), CodeEnum.USERNAME_EXIST.getMsg());
        }
        lambdaQueryWrapper.clear();
        lambdaQueryWrapper.eq(User::getPhonenumber, userDTO.getPhonenumber());
        if (!userMapper.selectList(lambdaQueryWrapper).isEmpty()) {
            return R.error(CodeEnum.PHONENUMBER_EXIST.getCode(), CodeEnum.PHONENUMBER_EXIST.getMsg());
        }
        lambdaQueryWrapper.clear();
        lambdaQueryWrapper.eq(User::getEmail, userDTO.getEmail());
        if (!userMapper.selectList(lambdaQueryWrapper).isEmpty()) {
            return R.error(CodeEnum.EMAIL_EXIST.getCode(), CodeEnum.EMAIL_EXIST.getMsg());
        }
        User user = new User();
        BeanUtil.copyProperties(userDTO, user, true);
        LocalDateTime createTime = LocalDateTime.now();
        user.setCreateBy(getCurrentLoginUser().getUserName());
        user.setCreateTime(createTime);
        try {
            userMapper.insert(user);
        } catch (Exception e) {
            throw new ServiceException(CodeEnum.CREATE_ERROR.getCode(), CodeEnum.CREATE_ERROR.getMsg());
        }
        return R.success();
    }


    @Override
//    @Caching(put={@CachePut(key="#userId")},evict={@CacheEvict(value="userCache1",allEntries = true)})
    public R edit(Integer userId, UserDTO userDTO) {
        LambdaUpdateWrapper<User> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        lambdaUpdateWrapper.eq(User::getUserId, userId);
        lambdaUpdateWrapper.set(User::getUserName, userDTO.getUserName());
        lambdaUpdateWrapper.set(User::getNickName, userDTO.getNickName());
        lambdaUpdateWrapper.set(User::getRole, userDTO.getRole());
        lambdaUpdateWrapper.set(User::getSex, userDTO.getSex());
        lambdaUpdateWrapper.set(User::getEmail, userDTO.getEmail());
        lambdaUpdateWrapper.set(User::getPhonenumber, userDTO.getPhonenumber());
        lambdaUpdateWrapper.set(User::getAddress, userDTO.getAddress());
        lambdaUpdateWrapper.set(User::getStatus, userDTO.getStatus());
        lambdaUpdateWrapper.set(User::getRemark, userDTO.getRemark());
        userMapper.update(new User(), lambdaUpdateWrapper);
        return R.success();
    }

    @Override
    public R isEmailExist(String email) {
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper();
        lambdaQueryWrapper.eq(User::getEmail, email);
        if (userMapper.exists(lambdaQueryWrapper)) {
            return R.error(CodeEnum.EMAIL_EXIST.getCode(), CodeEnum.EMAIL_EXIST.getMsg());
        } else {
            return R.success();
        }
    }

    @Override
    public R isPhonenumberExist(String phonenumber) {
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper();
        lambdaQueryWrapper.eq(User::getPhonenumber, phonenumber);
        if (userMapper.exists(lambdaQueryWrapper)) {
            return R.error(CodeEnum.PHONENUMBER_EXIST.getCode(), CodeEnum.PHONENUMBER_EXIST.getMsg());
        } else {
            return R.success();
        }
    }

    @Override
    public R isUsernameExist(String username) {
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper();
        lambdaQueryWrapper.eq(User::getUserName, username);
        if (userMapper.exists(lambdaQueryWrapper)) {
            return R.error(CodeEnum.USERNAME_EXIST.getCode(), CodeEnum.USERNAME_EXIST.getMsg());
        } else {
            return R.success();
        }
    }

    @Override
    public R verifyUserEmail(String userName, String email) {
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper();
        lambdaQueryWrapper.eq(User::getUserName, userName);
        lambdaQueryWrapper.eq(User::getEmail, email);
        if (!userMapper.exists(lambdaQueryWrapper)) {
            return R.error(CodeEnum.EMAIL_NOT_EXIST.getCode(), CodeEnum.EMAIL_NOT_EXIST.getMsg());
        } else {
            return R.success();
        }
    }

    @Override
    public R verifyPassword(String userName, String originalPassword) {
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper();
        lambdaQueryWrapper.eq(User::getUserName, userName);
        User user = userMapper.selectOne(lambdaQueryWrapper);
        System.out.println(passwordEncoder.matches(originalPassword, user.getPassword()));
        if (!passwordEncoder.matches(originalPassword, user.getPassword())) {
            return R.error(CodeEnum.ORIGINAL_PASSWORD_ERROR.getCode(), CodeEnum.ORIGINAL_PASSWORD_ERROR.getMsg());
        } else {
            return R.success();
        }
    }

    @Override
    public R updatePasswordByUserName(String userName, String password) {
        LambdaUpdateWrapper<User> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        lambdaUpdateWrapper.eq(User::getUserName, userName);
        LocalDateTime updateTime = LocalDateTime.now();
        lambdaUpdateWrapper.set(User::getPassword, passwordEncoder.encode(password));
        lambdaUpdateWrapper.set(User::getUpdateTime, updateTime);
        try {
            lambdaUpdateWrapper.set(StrUtil.isNotBlank(getCurrentLoginUser().getUserName()), User::getUpdateBy, getCurrentLoginUser().getUserName());
        } catch (NullPointerException e) {
            throw new RuntimeException(e);
        } finally {
            userMapper.update(new User(), lambdaUpdateWrapper);
            return R.success();
        }

    }


    @Override
    public R updateAvatarByUserName(String userName, String avatar) {
        LocalDateTime updateTime = LocalDateTime.now();
        LambdaUpdateWrapper<User> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        lambdaUpdateWrapper.eq(User::getUserName, userName);
        lambdaUpdateWrapper.set(User::getUpdateBy, getCurrentLoginUser().getUserName());
        lambdaUpdateWrapper.set(User::getUpdateTime, updateTime);
        lambdaUpdateWrapper.set(User::getAvatar, avatar);
        userMapper.update(new User(), lambdaUpdateWrapper);
        return R.success();
    }



    @Override
    public R register(UserDTO userDTO) {
        User user = new User();
        String encryptedPassword = passwordEncoder.encode(userDTO.getPassword());
        BeanUtil.copyProperties(userDTO, user, true);
        LocalDateTime createTime = LocalDateTime.now();
        user.setPassword(encryptedPassword);
        user.setCreateBy(user.getUserName());
        user.setCreateTime(createTime);
        user.setRole("ROLE_PATIENT");
        user.setAvatar("http://localhost:8088/file/fc1ecf15cb564468a9c498eaff3e4ef0.jpg");
        userMapper.insert(user);
        Patient patient=new Patient();
        patient.setUserId(user.getUserId());
        patientMapper.insert(patient);
        return R.success();
    }

    @Override
    public R generateCaptchaImage() {
        String captcha = defaultKaptcha.createText();
        System.out.println(captcha);
        BufferedImage image = defaultKaptcha.createImage(captcha);
        String base64Code = null;
        ByteArrayOutputStream out = null;
        try {
            out = new ByteArrayOutputStream();
            ImageIO.write(image, "jpg", out);
            base64Code = Base64.encodeBase64String(out.toByteArray());
        } catch (IOException e) {
            throw new ServiceException(CodeEnum.VERIFICATION_CODE_ERROR.getCode(), CodeEnum.VERIFICATION_CODE_ERROR.getMsg());
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    throw new ServiceException(CodeEnum.VERIFICATION_CODE_ERROR.getCode(), CodeEnum.VERIFICATION_CODE_ERROR.getMsg());
                }
            }
        }
        String uuid = IdUtil.fastSimpleUUID();
        RedisUtil.set(uuid, captcha);
        RedisUtil.expire(uuid, 60);
        Map<String, String> captchaMap = new HashMap<String, String>();
        captchaMap.put("uuid", uuid);
        captchaMap.put("codeUrl", "data:image/png;base64," + base64Code);
        return R.success(captchaMap);
    }


    @Override
    public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
        UserDTO userDTO = getUserByUserName(userName);
        if (userDTO == null) {
            throw new ServiceException(CodeEnum.NO_USER_EXIST.getCode(), CodeEnum.NO_USER_EXIST.getMsg());
        }
        if (userDTO.getStatus() == 0) {
            throw new ServiceException(CodeEnum.USER_LOCKED.getCode(), CodeEnum.USER_LOCKED.getMsg());
        }
        User user = new User();
        BeanUtil.copyProperties(userDTO, user, true);
        return new UserDetail(user);
    }

    @Override
    public User getCurrentLoginUser() {
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest httpServletRequest = servletRequestAttributes.getRequest();
        String token = httpServletRequest.getHeader("token");
        if (StrUtil.isNotBlank(token)) {
            String userId = JWT.decode(token).getAudience().get(0);
            return getById(Integer.parseInt(userId));
        } else {
            return null;
        }
    }

    @Override
    public R logout() {
        RedisUtil.delete(String.valueOf(getCurrentLoginUser().getUserId()));
        SecurityContextHolder.getContext().setAuthentication(null);
        return R.success();
    }

    @Override
    public void generateUserInfomationImportTemplate(HttpServletResponse response) {
        String templateName="用户信息导入模版";
        List<List<String>> headerList=new ArrayList<List<String>>();
        String firstHead="用户信息导入模版(提示：用户名称、用户邮箱、手机号码必填，其他可选)";
        List<String> head0=new ArrayList<>();
        head0.add(firstHead);
        head0.add("用户名称");
        headerList.add(head0);
        List<String> head1=new ArrayList<>();
        head1.add(firstHead);
        head1.add("用户昵称");
        headerList.add(head1);
        List<String> head2=new ArrayList<>();
        head2.add(firstHead);
        head2.add("用户邮箱");
        headerList.add(head2);
        List<String> head3=new ArrayList<>();
        head3.add(firstHead);
        head3.add("手机号码");
        headerList.add(head3);
        List<String> head4=new ArrayList<>();
        head4.add(firstHead);
        head4.add("联系地址");
        headerList.add(head4);
        List<String> head5=new ArrayList<>();
        head5.add(firstHead);
        head5.add("用户性别");
        headerList.add(head5);
        List<String> head6=new ArrayList<>();
        head6.add(firstHead);
        head6.add("角色唯一标识");
        headerList.add(head6);
        List<String> head7=new ArrayList<>();
        head7.add(firstHead);
        head7.add("备注");
        headerList.add(head7);
        ExcelUtils.generateTemplate(response,templateName,headerList);
    }






    @Override
    public void updateFailedAttempts(String userName)  {
      Integer failedAttempts=(Integer)redisTemplate.opsForValue().get("failedAttempts:"+userName);
       if(Objects.isNull(failedAttempts)||failedAttempts<5){
           redisTemplate.opsForValue().increment("failedAttempts:"+userName);
           redisTemplate.expire("failedAttempts:"+userName,10, TimeUnit.MINUTES);
           if(isAccountLocked(userName)){
               try {
                   Date currentDate=new Date();
                   Calendar calendar= Calendar.getInstance();
                   calendar.setTime(currentDate);
                   calendar.add(Calendar.MINUTE,10);
                   JobKey jobKey = JobKey.jobKey("unlockUserAccount:"+userName,"unlockUserAccount");
                   Class<Job> jobClass = (Class<Job>) Class.forName("com.example.framework.job.UserStatusMonitoringJob");
                   JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobKey).withIdentity("unlockUserAccount:"+userName, "unlockUserAccount").withDescription("unlockUserAccount:"+userName).build();
                   jobDetail.getJobDataMap().put("config", userName);
                   TriggerKey triggerKey = TriggerKey.triggerKey("unlockUserAccount:"+userName, "unlockUserAccount");
                   Trigger trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).startAt(calendar.getTime()).usingJobData("config", userName).withSchedule(SimpleScheduleBuilder.simpleSchedule().withRepeatCount(1).withIntervalInMinutes(1).withMisfireHandlingInstructionNowWithExistingCount()).build();
                   scheduler.scheduleJob(jobDetail,trigger);
               } catch (ClassNotFoundException e) {
                   throw new ServiceException(CodeEnum.TIMING_TASK_ERROR.getCode(),CodeEnum.TIMING_TASK_ERROR.getMsg());
               } catch (SchedulerException e) {
                   throw new ServiceException(CodeEnum.TIMING_TASK_ERROR.getCode(),CodeEnum.TIMING_TASK_ERROR.getMsg());
               }
               LambdaUpdateWrapper<User> lambdaUpdateWrapper=new LambdaUpdateWrapper<>();
               lambdaUpdateWrapper.eq(User::getUserName,userName);
               lambdaUpdateWrapper.set(User::getStatus,0);
               userMapper.update(new User(),lambdaUpdateWrapper);
           }
       }
    }

    @Override
    public void clearFailedAttempts(String userName){
        redisTemplate.delete("failedAttempts:"+userName);
    }

    @Override
    public boolean isAccountLocked(String userName){
        Integer failedAttempts=(Integer)redisTemplate.opsForValue().get("failedAttempts:"+userName);
        return failedAttempts!=null && failedAttempts>=5;
     }
}